#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__author__ = '汤小洋'

'''
作用域scope：指的是变量生效的区域

两种作用域：
1.全局作用域
   - 函数以外的区域都是全局作用域
   - 全局作用域在程序执行时创建，在程序执行结束时销毁
   - 在全局作用域中定义的变量，都是全局变量，可以在程序的任意位置被访问   
2.函数作用域（也称局部作用域）
   - 函数内的区域，每调用一次就会产生一个新的函数作用域
   - 函数作用域在函数调用时创建，在调用结束时销毁
   - 在函数作用域中定义的变量，都是局部变量，只能在函数内部被访问
   
变量的查找顺序：
    先在当前作用域中查找，如果没有则继续到上一级作用域中查找，直到查找全局作用域，如果还是没有，则会抛出异常
'''

a = 5  # 全局变量

if True:
    b = 2  # 全局变量，在Python中没有块级作用域


def fn():
    c = 8  # 局部变量
    print('函数内部：a=', a)
    print('函数内部：b=', b)
    print('函数内部：c=', c)


fn()

print('函数外部：a=', a)
print('函数外部：b=', b)
# print('函数外部：c=', c)
print('---------------------------------------------------')


# global关键字
def fn2():
    # a = 10  # 在函数中为变量赋值时，默认都是为局部变量赋值
    # 如果希望在函数中修改全局变量，则需要使用global来声明全局变量
    global a
    a = 10  # 此时修改的就是全局变量a
    print('函数内部：a=', a)


fn2()
print('函数外部：a=', a)
print('---------------------------------------------------')


'''
命名空间namespace：指的是变量存储的位置，每一个变量都需要存储到指定的命名空间中

每一个作用域都对应着一个命名空间
全局命名空间用来保存全局变量；函数命名空间用来保存函数中的变量
命名空间实际上就是一个字典，是一个专门用来存储变量的字典
'''

# locals() 获取当前作用域的命名空间
scope = locals()  # 在全局作用域中调用locals()，获取的就是全局命名空间
print(type(scope))  # dict类型
print(scope)

# 通过scope操作命名空间中的变量（不建议这样做）
print(a)
print(scope['a'])
scope['x'] = 1000  # 向字典中添加值就相当于在该作用域中创建了变量（但不建议这么做）
print(scope['x'])
# print(x)  # 虽然IDEA会报错，但实际上可以获取到值


def fn3():
    a = 666
    scope = locals()  # 在函数中调用locals()，获取的就是函数命名空间
    print(scope)
    scope['y'] = 20
    print(scope['y'])
    # print(y)  # 无法通过该方式获取值，函数命名空间不支持

    # globals() 可以用来在任意位置获取全局命名空间
    global_scope = globals()
    print(global_scope['a'])
    global_scope['a'] = 30
    print(global_scope)


fn3()
